package com.zl.iot.docking.service;

import com.aliyun.iot20180120.models.*;
import com.zl.iot.docking.config.ServerInfo;
import com.zl.iot.docking.consts.ModuleStatus;
import com.zl.iot.docking.exception.MyExceptionNew;
import com.zl.iot.docking.exception.ZLExcep;
import com.zl.iot.docking.model.*;
import com.zl.iot.docking.util.AliUtil;
import com.zl.iot.docking.util.Base64Util;
import com.zl.iot.docking.util.HmacSHA1;
import org.slf4j.Logger;

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;

import static com.zl.iot.docking.consts.AliCode.*;

/**
 * 参考阿里官方文档 https://help.aliyun.com/document_detail/69893.htm?spm=a2c4g.11186623.0.0.5a347792yDTchM#reference-kd4-l4z-wdb
 * @author chenzy
 * @since 2021-11-10
 **/
public class AliProductRequest {
    private static final Logger logger = ServerInfo.getInfoLog();
    private final AliProductServer product;
    private final AliServerVO aliServer;
    private static final Map<String,AliProductRequest> requestMap = new HashMap<>();

    private AliProductRequest(AliProductServer product) {
        this.product = product;
        this.aliServer = product.getAliServer();
    }

    public static synchronized AliProductRequest build(AliProductServer product) {
        var ckey = product.getCkey();
        if (!requestMap.containsKey(ckey)) {
            requestMap.put(ckey, new AliProductRequest(product));
        }
        return requestMap.get(ckey);
    }

    public static void remove(String ckey) {
        requestMap.remove(ckey);
    }
    /*https://next.api.aliyun.com/api/Iot/2018-01-20/GetDeviceStatus?lang=JAVA&tab=DOC&params={%22IotInstanceId%22:%22iot-040a09k9%22,%22IotId%22:%222201060600014AF%22}&sdkStyle=dara */
    private ModuleStatusVO getStatus(GetDeviceStatusRequest statusRequest) {
        try {
            var body = AliUtil.getClient(aliServer).getDeviceStatus(statusRequest).body;
            if (body.success) {
                return new ModuleStatusVO(getStatusEnum(body.data.status), body.data.timestamp.toString());
            } else {
                switch (body.code) {
                    case MachineNoExist:
                        return new ModuleStatusVO().setStatus(ModuleStatus.NoRegist);
                    default:
                        throw ZLExcep.error("访问阿里云异常,没有捕获的阿里云提示:" + body.code);
                }
            }
        } catch (MyExceptionNew e) {
            throw e;
        } catch (Exception e) {
            throw ZLExcep.error("访问阿里云异常,获取4G模块状态失败", e);
        }
    }

    /*private List<ModuleStatusOut> getStatus(BatchGetDeviceStateRequest statusRequest) {
        try {
            var body = AliUtil.getClient(aliServer).batchGetDeviceState(statusRequest).body;
            if (body.success) {
                body.deviceStatusList.deviceStatus.stream().map(item->{
                    return new ModuleStatusOut(getStatusEnum(item.status), item.lastOnlineTime);
                });


            } else {
                switch (body.code) {
                    default:
                        throw MyExceptionNew.info("访问阿里云异常,没有捕获的阿里云提示:" + body.code);
                }
            }
        } catch (MyExceptionNew e) {
            throw e;
        } catch (Exception e) {
            throw PassExcep.error("访问阿里云异常,获取4G模块状态失败", e);
        }
        return null;
    }*/
/*    public Object getStatusByModuleId(String moduleId) {
        return getStatus(new GetDeviceStatusRequest()
                .setIotInstanceId(AliUtil.getInstanceId(aliServer))
                .setIotId(moduleId));
    }*/

    public ModuleStatusVO getStatusByMachineId(String machineId) {
        return getStatus(new GetDeviceStatusRequest()
                .setIotInstanceId(AliUtil.getInstanceId(aliServer))
                .setProductKey(product.getCkey())
                    .setDeviceName(machineId));
    }

    private ModuleStatus getStatusEnum(String status) {
        switch (status) {
            case "ONLINE":
                return ModuleStatus.Online;/*设备在线*/
            case "OFFLINE":
                return ModuleStatus.Offline;/*设备离线*/
            case "UNACTIVE":
                return ModuleStatus.NoActiv;/*设备未激活*/
            case "DISABLE":
                return ModuleStatus.Disable;/*设备已禁用*/
            default:
                return ModuleStatus.Other;
        }
    }

    /*注册前校验*/
    private void beforeReistCheck(String investorId,String deviceVer) {
        if (aliServer.getCurMachineNum() >= aliServer.getMaxMachineNum()) {
            AliProductRequest.remove(product.ckey);
            ZLExcep.throwInfo("["+investorId+"]["+deviceVer+"]选择的实例最大允许注册设备数:" + aliServer.getMaxMachineNum() + ",当前已注册设备:" + aliServer.getCurMachineNum()+",请升容或更换实例");
        }
    }
    private void after(BiConsumer<String, Boolean> consumer, String instanceId, String ckey, Boolean add) {
        AliProductRequest.remove(ckey);
        consumer.accept(instanceId, add);
    }

    /*注册成功后，执行consumer,实例当前设备数量+1*/
    public RegistResult regist(String machineId,String investorId,String deviceVer, BiConsumer<String, Boolean> consumer) {
        beforeReistCheck(investorId,deviceVer);
        var productKey = product.getCkey();
        var instanceId = AliUtil.getInstanceId(aliServer);
        var client = AliUtil.getClient(aliServer);
        try {
            var response = client.registerDevice(new RegisterDeviceRequest()
                    .setIotInstanceId(instanceId)
                    .setProductKey(productKey)
                    .setDeviceName(machineId));
//            logger.info(JsonUtil.model2Str(response).get());
            String iotId, machineSecretOrig, deviceName;
            if (response.body.success) {
                iotId = response.body.data.iotId;
                machineSecretOrig = response.body.data.deviceSecret;
                deviceName = response.body.data.deviceName;
            } else {
                switch (response.body.code) {
                    case MachineExisted: {
                        var queryBody = client.queryDeviceInfo(new QueryDeviceInfoRequest()
                                .setIotInstanceId(instanceId)
                                .setProductKey(productKey)
                                .setDeviceName(machineId)).body;
                        if (!queryBody.success) {
                            throw ZLExcep.error("重复注册设备,阿里云查询设备信息异常" + queryBody.errorMessage);
                        }
                        iotId = queryBody.data.iotId;
                        machineSecretOrig = queryBody.data.deviceSecret;
                        deviceName = queryBody.data.deviceName;
                        break;
                    }
                    default:
                        throw ZLExcep.info("注册设备异常,没有捕获的阿里云提示:" + response.body.code);
                }
            }
            var deviceSecret = encryptTriplet(productKey, deviceName, machineSecretOrig);
            after(consumer,instanceId,productKey,true);
            return new RegistResult(iotId, deviceSecret, machineSecretOrig);
        } catch (MyExceptionNew e) {
            throw e;
        } catch (Exception e) {
            throw ZLExcep.error("注册设备失败", e);
        }
    }
    /**
     * 计算三元组加密后的信息
     *
     * @author 曾泽亦
     */
    private static String encryptTriplet(String productKey, String DeviceName, String DeviceSecret) {
        String content = String.format("clientId%sdeviceName%sproductKey%s", DeviceName, DeviceName, productKey);
        String encryptContent = HmacSHA1.hmacSha1(content, DeviceSecret);
        return encryptContent;
    }
    /*注销成功后，执行consumer,实例当前设备数量-1*/
    public UnregistOldResult unregist(String moduleId, BiConsumer<String, Boolean> consumer) {
        var productKey = product.getCkey();
        var instanceId = AliUtil.getInstanceId(aliServer);
        var client = AliUtil.getClient(aliServer);
        try {
            var body = client.deleteDevice(new DeleteDeviceRequest()
                    .setIotInstanceId(instanceId)
                    .setProductKey(productKey)
                    .setDeviceName(moduleId)).body;
//            logger.info(JsonUtil.model2Str(body).get());
            if (!body.success) {
                switch (body.code) {
                    case IotInstanceStatusForbidden:
                    case IotInstanceNotFound:
                    case MachineNoExist: {
                        after(consumer,instanceId,productKey,false);
                        return new UnregistOldResult().setCode(body.code)
                                .setRequestId(body.requestId)
                                .setSuccess("true");
                    }
                    default:
                        throw ZLExcep.info("注销设备异常,没有捕获的阿里云提示:" + body.code);
                }
            }
            after(consumer,instanceId,productKey,false);
            return new UnregistOldResult().setCode(body.code)
                    .setRequestId(body.requestId)
                    .setSuccess("true");
        } catch (MyExceptionNew e) {
            throw e;
        } catch (Exception e) {
            throw ZLExcep.error("注册设备失败", e);
        }
    }
    public PubResponseBody send(String moduleName, String content) {
        var ckey = product.getCkey();
        var topic = "/" + ckey + "/" + moduleName + "/user/get";
        var encodeContent = Base64Util.encode(content);
        logger.info("发送消息给iot。topic:{},content:{},encode:{}", topic, content, encodeContent);
        try {
            var body = AliUtil.getClient(aliServer)
                    .pub(new PubRequest()
                            .setIotInstanceId(AliUtil.getInstanceId(aliServer))
                            .setProductKey(ckey)
                            .setTopicFullName(topic)
                            .setMessageContent(encodeContent)).body;
//            logger.info(JsonUtil.model2Str(body).get());
            if (!body.success) {
                throw ZLExcep.error("4G通道发送消息给设备失败:" + body.errorMessage);
            }
//            return JsonUtil.model2Model(body, StringMap.class).get();
            return body;
        } catch (MyExceptionNew e) {
            throw e;
        } catch (Exception e) {
            throw ZLExcep.error("发送阿里云消息失败:" + e.getMessage(), e);
        }
    }
}
